/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.django.debug.ui.actions;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.internal.ui.views.console.ProcessConsoleManager;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IOConsole;
import org.eclipse.ui.console.IOConsoleOutputStream;
import org.eclipse.ui.dialogs.FilteredItemsSelectionDialog;
import org.eclipse.ui.internal.ide.dialogs.OpenResourceDialog;
import org.python.pydev.core.IPythonPathNature;
import org.python.pydev.core.docutils.StringUtils;
import org.python.pydev.core.log.Log;
import org.python.pydev.django.launching.DjangoConstants;
import org.python.pydev.django.launching.PythonFileRunner;
import org.python.pydev.editor.actions.PyAction;
import org.python.pydev.plugin.nature.PythonNature;
/**
* Base class for django actions.
*/
public abstract class DjangoAction implements IObjectActionDelegate {
/**
* The project that was selected (may be null).
*/
protected IProject selectedProject;
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
// empty
}
/**
* Actually remove the python nature from the project.
*/
public abstract void run(IAction action);
/**
* A project was just selected
*/
public void selectionChanged(IAction action, ISelection selection) {
selectedProject = null;
if (selection.isEmpty() || !(selection instanceof IStructuredSelection)) {
return;
}
IStructuredSelection selections = (IStructuredSelection) selection;
Object project = selections.getFirstElement();
if (!(project instanceof IProject)) {
return;
}
this.selectedProject = (IProject) project;
}
public void setSelectedProject(IProject selectedProject) {
this.selectedProject = selectedProject;
}
/**
* May be used to run some command that uses the manage.py file.
*/
@SuppressWarnings("restriction")
public ILaunch launchDjangoCommand(final String command, boolean refreshAndShowMessageOnFinish) {
PythonNature nature = PythonNature.getPythonNature(selectedProject);
if (nature == null) {
MessageDialog.openError(PyAction.getShell(), "PyDev nature not found",
"Unable to perform action because the Pydev nature is not properly set.");
return null;
}
IPythonPathNature pythonPathNature = nature.getPythonPathNature();
String manageVarible = null;
Map<String, String> variableSubstitution = null;
try {
variableSubstitution = pythonPathNature.getVariableSubstitution();
manageVarible = variableSubstitution.get(DjangoConstants.DJANGO_MANAGE_VARIABLE);
} catch (Exception e1) {
throw new RuntimeException(e1);
}
if (manageVarible == null) {
manageVarible = askNewManageSubstitution(pythonPathNature, variableSubstitution, com.aptana.shared_core.string.StringUtils.format(
"Unable to perform action because the %s \n" + "substitution variable is not set.\n\n"
+ "Please select the manage.py to be used to run the action.",
DjangoConstants.DJANGO_MANAGE_VARIABLE));
if (manageVarible == null) {
return null;
}
}
IFile manageDotPy = selectedProject.getFile(manageVarible);
if (manageDotPy == null || !manageDotPy.exists()) {
manageVarible = askNewManageSubstitution(pythonPathNature, variableSubstitution, com.aptana.shared_core.string.StringUtils.format(
"Unable to perform action because the %s \n"
+ "substitution variable is set to a non existing file.\n\n"
+ "Please select the manage.py to be used to run the action.",
DjangoConstants.DJANGO_MANAGE_VARIABLE));
if (manageVarible == null) {
return null;
}
//we shouldn't need to validate again (he can't choose a wrong file there right?)
manageDotPy = selectedProject.getFile(manageVarible);
}
final IFile finalManageDotPy = manageDotPy;
try {
ILaunch launch = PythonFileRunner.launch(manageDotPy, command);
//After the command completes, refresh and put message for user.
final IProcess[] processes = launch.getProcesses();
ProcessConsoleManager consoleManager = DebugUIPlugin.getDefault().getProcessConsoleManager();
if (processes.length >= 1) {
IConsole console = consoleManager.getConsole(processes[0]);
final IOConsoleOutputStream outputStream = ((IOConsole) console).newOutputStream();
Job j = new Job("Refresh on finish") {
protected IStatus run(IProgressMonitor monitor) {
boolean allTerminated = false;
while (!allTerminated) {
allTerminated = true;
for (IProcess p : processes) {
if (!p.isTerminated()) {
allTerminated = false;
break;
}
}
synchronized (this) {
try {
this.wait(50);
} catch (InterruptedException e) {
}
}
}
try {
outputStream.write(com.aptana.shared_core.string.StringUtils.format("Finished \""
+ finalManageDotPy.getLocation().toOSString() + " " + command + "\" execution."));
} catch (IOException e1) {
Log.log(e1);
}
try {
outputStream.close();
} catch (IOException e1) {
Log.log(e1);
}
try {
selectedProject.refreshLocal(IResource.DEPTH_INFINITE, null);
} catch (CoreException e) {
Log.log(e);
}
return Status.OK_STATUS;
}
};
j.setSystem(true);
j.schedule();
}
return launch;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Asks the user to select a new manage.py file and saves that selection.
*/
private String askNewManageSubstitution(IPythonPathNature pythonPathNature,
Map<String, String> variableSubstitution, String message) {
String manageVarible = null;
OpenResourceDialog manageSelectionDialog = createManageSelectionDialog(message);
if (manageSelectionDialog.open() == OpenResourceDialog.OK) {
Object firstResult = manageSelectionDialog.getFirstResult();
if (firstResult instanceof IFile) {
IFile iFile = (IFile) firstResult;
IPath projectRelativePath = iFile.getProjectRelativePath();
manageVarible = projectRelativePath.toPortableString();
variableSubstitution.put(DjangoConstants.DJANGO_MANAGE_VARIABLE, manageVarible);
try {
pythonPathNature.setVariableSubstitution(variableSubstitution);
} catch (Exception e) {
Log.log(e);
}
} else {
Log.log("Error. Expected IFile selected. Found: " + firstResult.getClass());
return null;
}
} else { //dialog cancelled
return null;
}
return manageVarible;
}
private OpenResourceDialog createManageSelectionDialog(String message) {
OpenResourceDialog resourceDialog = new OpenResourceDialog(PyAction.getShell(), selectedProject, IResource.FILE);
try {
//Hack warning: changing the multi internal field to false because we don't want a multiple selection
//(but the OpenResourceDialog didn't make available an API to change that -- even though
//it'd be possible to create a FilteredItemsSelectionDialog in single selection mode)
Field field = FilteredItemsSelectionDialog.class.getDeclaredField("multi");
field.setAccessible(true);
field.set(resourceDialog, false);
} catch (Throwable e) {
//just ignore any error here
}
resourceDialog.setInitialPattern("manage.py");
resourceDialog.setMessage(message);
return resourceDialog;
}
}